/*
 * Copyright (C) 2025 ameise <ameise.wang@gmail.com> - All Rights Reserved
 *
 * This file is part of e3ds.
 *
 * e3ds is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * e3ds is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with e3ds. If not, see <https://www.gnu.org/licenses/>.
 */

package rtree

type RTree struct {
	root *node
	objs map[Tag]*Entity
}

func (ts *RTree) Objects() map[Tag]*Entity {
	return ts.objs
}

func (ts *RTree) Insert(min, max Point, ctx Context) *Entity {
	obj := newEntity(newRect(min, max), ctx)
	grown := ts.root.insert(obj)
	if grown {
		ts.root.rect().merge(obj.rect())
	}
	if len(ts.root.children) == maxObject {
		left := ts.root
		right := newNode(left.height)
		root := newNode(left.height + 1)

		left.splitTo(right)
		root.addChildren(left)
		root.addChildren(right)
		root.rect().merge(left.rect(), right.rect())
		ts.root = root
	}

	ts.objs[obj.id] = obj
	return obj
}

func (ts *RTree) Delete(obj *Entity) (Context, bool) {
	return ts.DeleteWithTag(obj.UniqueID())
}

func (ts *RTree) DeleteWithTag(id Tag) (Context, bool) {
	obj, ok := ts.objs[id]
	if !ok {
		return nil, false
	}
	ctx := obj.ctx
	obj.father.delete(obj)
	if ts.root.height > 0 && len(ts.root.children) == 1 {
		oldRoot := ts.root
		var newRoot *node
		for _, c := range oldRoot.children {
			newRoot = c.(*node)
		}
		clear(oldRoot.children)
		newRoot.father = nil
		ts.root = newRoot
		oldRoot.free()
	}
	if ts.root.height == 0 && len(ts.root.children) == 0 {
		ts.root.box.clean()
	}
	delete(ts.objs, id)
	return ctx, true
}

func (ts *RTree) FindOverlaps(min, max Point, cb func(*Entity) bool) {
	ts.search(min, max, false, cb)
}

func (ts *RTree) FindOverlapsOrAdj(min, max Point, cb func(*Entity) bool) {
	ts.search(min, max, true, cb)
}

func (ts *RTree) HasOverlap(min, max Point) bool {
	return ts.hasOverlap(min, max, false)
}

func (ts *RTree) HasOverlapOrAdj(min, max Point) bool {
	return ts.hasOverlap(min, max, true)
}

func (ts *RTree) Range(cb func(*Entity) bool) {
	for _, obj := range ts.objs {
		if !cb(obj) {
			return
		}
	}
}

func (ts *RTree) search(min, max Point, withAdj bool, cb func(*Entity) bool) {
	if len(ts.objs) == 0 {
		return
	}
	b := newRect(min, max)
	ts.root.isOverlap(&b, withAdj, cb)
}

func (ts *RTree) hasOverlap(min, max Point, withAdj bool) bool {
	if len(ts.objs) == 0 {
		return false
	}
	b := newRect(min, max)
	count := 0
	f := func(*Entity) bool {
		count++
		return false
	}
	ts.root.isOverlap(&b, withAdj, f)
	return count > 0
}
